﻿using System;
using System.Diagnostics;

namespace SfsTools.Core
{
    /// <summary>
    /// Provides static methods to convert to/from SFS timestamps.
    /// </summary>
    public struct TimeStamp
    {
        #region Properties

        /// <summary>
        /// Contains the timestamp value.
        /// </summary>
        private readonly long FValue;

        /// <summary>
        /// The zero date.
        /// </summary>
        public static readonly DateTime ZeroDate = new DateTime(1970, 1, 1, 0, 0, 0);

        #endregion

        #region Methods

        /// <summary>
        /// Converts the <see cref="TimeStamp"/> to a <see cref="DateTime"/>.
        /// </summary>
        /// <returns>The date and time it represents.</returns>
        /// <remarks>The conversion may be inexact.</remarks>
        [DebuggerStepThrough]
        public DateTime ToDate()
        {
            long value = FValue;
            int fraction = (int)(value % 65536); value /= 65536;
            int seconds = (int)(value % 60); value /= 60;
            int minutes = (int)(value % 60); value /= 60;
            int hours = (int)(value % 24); value /= 24;
            int days = (int)value;
            TimeSpan ts = new TimeSpan(days, hours, minutes, seconds, Math.Min(fraction / 65, 999));
            return ZeroDate + ts;
        }

        /// <summary>
        /// Converts the <see cref="TimeStamp"/> to a <see cref="TimeSpan"/>.
        /// </summary>
        /// <returns>The time span it represents.</returns>
        /// <remarks>The conversion may be inexact.</remarks>
        [DebuggerStepThrough]
        public TimeSpan ToTimeSpan()
        {
            long value = FValue;
            int fraction = (int)(value % 65536); value /= 65536;
            int seconds = (int)(value % 60); value /= 60;
            int minutes = (int)(value % 60); value /= 60;
            int hours = (int)(value % 24); value /= 24;
            int days = (int)value;
            return new TimeSpan(days, hours, minutes, seconds, Math.Min(fraction / 65, 999));
        }

        /// <summary>
        /// Convets the <see cref="TimeStamp"/> to an <see cref="Int64"/>.
        /// </summary>
        /// <returns></returns>
        /// <remarks>The conversion is exact.</remarks>
        [DebuggerStepThrough]
        public Int64 ToInt64()
        {
            return FValue;
        }

        /// <summary>
        /// Converts a <see cref="DateTime"/> value to a <see cref="TimeStamp"/>.
        /// </summary>
        /// <param name="value">The date and time.</param>
        /// <returns>The timestamp.</returns>
        /// <remarks>The conversion may be inexact.</remarks>
        [DebuggerStepThrough]
        public static TimeStamp FromDate(DateTime value)
        {
            TimeSpan delta = value - ZeroDate;
            long value0 = ((((delta.Days * 24L) +
                delta.Hours) * 60 + delta.Minutes) * 60 + delta.Seconds) * 65536 + delta.Milliseconds * 65;
            return new TimeStamp(value0);
        }

        /// <summary>
        /// Gets the current date and time as a <see cref="TimeStamp"/>.
        /// </summary>
        /// <returns>The timestamp.</returns>
        /// <remarks>The conversion may be inexact.</remarks>
        public static TimeStamp Now
        {
            [DebuggerStepThrough]
            get
            {
                return FromDate(DateTime.Now);
            }
        }

        /// <summary>
        /// Converts this <see cref="TimeStamp"/> to a <see cref="String"/>.
        /// </summary>
        /// <returns></returns>
        [DebuggerStepThrough]
        public override string ToString()
        {
            return ToDate().ToString(new String('o', 1));
        }

        #endregion

        #region Constructors
        /// <overloads>Initializes a new instance of <see cref="TimeStamp"/>.</overloads>
        /// <summary>
        /// Initializes a new instance of <see cref="TimeStamp"/>.
        /// </summary>
        [DebuggerStepThrough]
        public TimeStamp(long value)
        {
            FValue = value;
        }
        ///<summary>
        /// Initializes a new instance of <see cref="TimeStamp"/>.
        ///</summary>
        ///<param name="year">The year.</param>
        ///<param name="month">The month.</param>
        ///<param name="day">The day.</param>
        [DebuggerStepThrough]
        public TimeStamp(int year, int month, int day)
        {
            DateTime date = new DateTime(year, month, day);
            TimeSpan delta = date - ZeroDate;
            long value0 = ((((delta.Days * 24L) +
                delta.Hours) * 60 + delta.Minutes) * 60 + delta.Seconds) * 65536 + delta.Milliseconds * 65;
            FValue = value0;
        }
        ///<summary>
        /// Initializes a new instance of <see cref="TimeStamp"/>.
        ///</summary>
        ///<param name="year">The year.</param>
        ///<param name="month">The month.</param>
        ///<param name="day">The day.</param>
        /// <param name="hour">The hour.</param>
        [DebuggerStepThrough]
        public TimeStamp(int year, int month, int day, int hour)
        {
            DateTime date = new DateTime(year, month, day, hour, 0, 0);
            TimeSpan delta = date - ZeroDate;
            long value0 = ((((delta.Days * 24L) +
                delta.Hours) * 60 + delta.Minutes) * 60 + delta.Seconds) * 65536 + delta.Milliseconds * 65;
            FValue = value0;
        }
        ///<summary>
        /// Initializes a new instance of <see cref="TimeStamp"/>.
        ///</summary>
        ///<param name="year">The year.</param>
        ///<param name="month">The month.</param>
        ///<param name="day">The day.</param>
        /// <param name="hour">The hour.</param>
        /// <param name="minute">The minute.</param>
        [DebuggerStepThrough]
        public TimeStamp(int year, int month, int day, int hour, int minute)
        {
            DateTime date = new DateTime(year, month, day, hour, minute, 0);
            TimeSpan delta = date - ZeroDate;
            long value0 = ((((delta.Days * 24L) +
                delta.Hours) * 60 + delta.Minutes) * 60 + delta.Seconds) * 65536 + delta.Milliseconds * 65;
            FValue = value0;
        }
        ///<summary>
        /// Initializes a new instance of <see cref="TimeStamp"/>.
        ///</summary>
        ///<param name="year">The year.</param>
        ///<param name="month">The month.</param>
        ///<param name="day">The day.</param>
        /// <param name="hour">The hour.</param>
        /// <param name="minute">The minute.</param>
        /// <param name="second">The seconds.</param>
        [DebuggerStepThrough]
        public TimeStamp(int year, int month, int day, int hour, int minute, int second)
        {
            DateTime date = new DateTime(year, month, day, hour, minute, second);
            TimeSpan delta = date - ZeroDate;
            long value0 = ((((delta.Days * 24L) +
                delta.Hours) * 60 + delta.Minutes) * 60 + delta.Seconds) * 65536 + delta.Milliseconds * 65;
            FValue = value0;
        }
        ///<summary>
        /// Initializes a new instance of <see cref="TimeStamp"/>.
        ///</summary>
        ///<param name="year">The year.</param>
        ///<param name="month">The month.</param>
        ///<param name="day">The day.</param>
        /// <param name="hour">The hour.</param>
        /// <param name="minute">The minute.</param>
        /// <param name="second">The seconds.</param>
        /// <param name="milliSecond">The milliseconds.</param>
        [DebuggerStepThrough]
        public TimeStamp(int year, int month, int day, int hour, int minute, int second, int milliSecond)
        {
            DateTime date = new DateTime(year, month, day, hour, minute, second, milliSecond);
            TimeSpan delta = date - ZeroDate;
            long value0 = ((((delta.Days * 24L) +
                delta.Hours) * 60 + delta.Minutes) * 60 + delta.Seconds) * 65536 + delta.Milliseconds * 65;
            FValue = value0;
        }
        #endregion
    }
}
